home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / ms_sh21s.zip / SH210 / SRC / SH8.C < prev    next >
C/C++ Source or Header  |  1992-12-14  |  18KB  |  945 lines

  1. /* MS-DOS SHELL - Unix File I/O Emulation
  2.  *
  3.  * MS-DOS SHELL - Copyright (c) 1990,1,2 Data Logic Limited
  4.  *
  5.  * This code is subject to the following copyright restrictions:
  6.  *
  7.  * 1.  Redistribution and use in source and binary forms are permitted
  8.  *     provided that the above copyright notice is duplicated in the
  9.  *     source form and the copyright notice in file sh6.c is displayed
  10.  *     on entry to the program.
  11.  *
  12.  * 2.  The sources (or parts thereof) or objects generated from the sources
  13.  *     (or parts of sources) cannot be sold under any circumstances.
  14.  *
  15.  *    $Header: /usr/users/istewart/src/shell/sh2.1/RCS/sh8.c,v 2.4 1992/12/14 10:54:56 istewart Exp $
  16.  *
  17.  *    $Log: sh8.c,v $
  18.  *    Revision 2.4  1992/12/14  10:54:56  istewart
  19.  *    BETA 215 Fixes and 2.1 Release
  20.  *
  21.  *    Revision 2.3  1992/11/06  10:03:44  istewart
  22.  *    214 Beta test updates
  23.  *
  24.  *    Revision 2.2  1992/07/16  14:33:34  istewart
  25.  *    Beta 212 Baseline
  26.  *
  27.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  28.  *    211 Beta updates
  29.  *
  30.  *    Revision 2.0  1992/04/13  17:39:09  Ian_Stewartson
  31.  *    MS-Shell 2.0 Baseline release
  32.  *
  33.  */
  34.  
  35. #include <sys/types.h>
  36. #include <sys/stat.h>
  37. #include <stdio.h>
  38. #include <signal.h>
  39. #include <errno.h>
  40. #include <setjmp.h>
  41. #include <stdlib.h>
  42. #include <fcntl.h>
  43. #include <stdarg.h>
  44. #include <string.h>
  45. #include <unistd.h>
  46. #include <limits.h>
  47. #include <dirent.h>
  48. #include <ctype.h>
  49. #ifdef OS2
  50. #define INCL_DOSSESMGR
  51. #include <os2.h>            /* DOS functions declarations       */
  52. #else
  53. #include <dos.h>            /* DOS functions declarations       */
  54. #endif
  55. #include "sh.h"
  56.  
  57. #define F_START        4
  58.  
  59. static char    *nopipe = "can't create pipe - try again\n";
  60.  
  61. /* List of open files to allow us to simulate the Unix open and unlink
  62.  * operation for temporary files
  63.  */
  64.  
  65. typedef struct flist {
  66.     struct flist    *fl_next;    /* Next link            */
  67.     char        *fl_name;    /* File name            */
  68.     bool        fl_close;    /* Delete on close flag        */
  69.     int            fl_size;    /* Size of fl_fd array        */
  70.     int            fl_count;    /* Number of entries in array    */
  71.     int            fl_mode;    /* File open mode        */
  72.     int            *fl_fd;        /* File ID array (for dup)    */
  73. } s_flist;
  74.  
  75. static s_flist        *list_start = (s_flist *)NULL;
  76. static s_flist        *near find_entry (int);
  77.  
  78. /*
  79.  * Open a file and add it to the Open file list.  Errors are the same as
  80.  * for a normal open.
  81.  */
  82.  
  83. int S_open (bool d_flag, char *name, int mode, ...)
  84. {
  85.     va_list        ap;
  86.     int            pmask;
  87.     s_flist        *fp = (s_flist *)NULL;
  88.     int            *f_list = (int *)NULL;
  89.     char        *f_name = (char *)NULL;
  90.     void        (*save_signal)(int);
  91.  
  92. /* Check the permission mask if it exists */
  93.  
  94.     va_start (ap, mode);
  95.     pmask = va_arg (ap, int);
  96.     va_end (ap);
  97.  
  98. /* Check this is a valid file name */
  99.  
  100.     CheckDOSFileName (name);
  101.  
  102. /* Grap some space.  If it fails, free space and return an error */
  103.  
  104.     if (((fp = (s_flist *) GetAllocatedSpace (sizeof (s_flist)))
  105.         == (s_flist *)NULL) ||
  106.     ((f_list = (int *) GetAllocatedSpace (sizeof (int) * F_START))
  107.         == (int *)NULL) ||
  108.     ((f_name = StringSave (name)) == null))
  109.     {
  110.     if (f_list == (int *)NULL)
  111.         ReleaseMemoryCell ((void *)f_list);
  112.  
  113.     if (fp == (s_flist *)NULL)
  114.         ReleaseMemoryCell ((void *)fp);
  115.  
  116.     errno = ENOMEM;
  117.     return -1;
  118.     }
  119.  
  120. /* Disable signals */
  121.  
  122.     save_signal = signal (SIGINT, SIG_IGN);
  123.  
  124. /* Set up the structure.  Change two Unix device names to the DOS
  125.  * equivalents and disable create
  126.  */
  127.  
  128.     if (strnicmp (name, DeviceNameHeader, LEN_DEVICE_NAME_HEADER) == 0)
  129.     {
  130.     if (stricmp (&name[5], "tty") == 0)
  131.         strcpy (&name[5], "con");
  132.  
  133.     else if (stricmp (&name[5], "null") == 0)
  134.         strcpy (&name[5], "nul");
  135.  
  136.     mode &= ~(O_CREAT | O_TRUNC);
  137.     }
  138.  
  139.     fp->fl_name  = strcpy (f_name, name);
  140.     fp->fl_close = d_flag;
  141.     fp->fl_size  = F_START;
  142.     fp->fl_count = 1;
  143.     fp->fl_fd    = f_list;
  144.     fp->fl_mode  = mode;
  145.  
  146. /* Open the file */
  147.  
  148.     if ((fp->fl_fd[0] = open (name, mode, pmask)) < 0)
  149.     {
  150.     pmask = errno;
  151.     ReleaseMemoryCell ((void *)f_name);
  152.     ReleaseMemoryCell ((void *)f_list);
  153.     ReleaseMemoryCell ((void *)fp);
  154.     errno = pmask;
  155.     pmask = -1;
  156.     }
  157.  
  158. /* Make sure everything is in area 0 */
  159.  
  160.     else
  161.     {
  162.     SetMemoryAreaNumber ((void *)fp, 0);
  163.     SetMemoryAreaNumber ((void *)f_list, 0);
  164.  
  165. /* List into the list */
  166.  
  167.     fp->fl_next   = list_start;
  168.     list_start = fp;
  169.  
  170. /* Return the file descriptor */
  171.  
  172.     pmask = fp->fl_fd[0];
  173.     }
  174.  
  175. /* Restore signals */
  176.  
  177.     signal (SIGINT, save_signal);
  178.  
  179.     return pmask;
  180. }
  181.  
  182. /*
  183.  * Scan the File list for the appropriate entry for the specified ID
  184.  */
  185.  
  186. static s_flist * near find_entry (int fid)
  187. {
  188.     s_flist    *fp = list_start;
  189.     int        i;
  190.  
  191.     while (fp != (s_flist *)NULL)
  192.     {
  193.     for (i = 0; i < fp->fl_count; i++)
  194.     {
  195.         if (fp->fl_fd[i] == fid)
  196.         return fp;
  197.     }
  198.  
  199.     fp = fp->fl_next;
  200.     }
  201.  
  202.     return (s_flist *)NULL;
  203. }
  204.  
  205. /* Close the file
  206.  *
  207.  * We need a version of close that does everything but close for dup2 as
  208.  * new file id is closed.  If c_flag is TRUE, close the file as well.
  209.  */
  210.  
  211. int S_close (int fid, bool c_flag)
  212. {
  213.     s_flist    *fp = find_entry (fid);
  214.     s_flist    *last = (s_flist *)NULL;
  215.     s_flist    *fp1 = list_start;
  216.     int        i, serrno;
  217.     bool    release = TRUE;
  218.     bool    delete = FALSE;
  219.     char    *fname;
  220.     void    (*save_signal)(int);
  221.  
  222. /* Disable signals */
  223.  
  224.     save_signal = signal (SIGINT, SIG_IGN);
  225.  
  226. /* Find the entry for this ID */
  227.  
  228.     if (fp != (s_flist *)NULL)
  229.     {
  230.     for (i = 0; i < fp->fl_count; i++)
  231.     {
  232.         if (fp->fl_fd[i] == fid)
  233.         fp->fl_fd[i] = -1;
  234.  
  235.         if (fp->fl_fd[i] != -1)
  236.         release = FALSE;
  237.     }
  238.  
  239. /* Are all the Fids closed ? */
  240.  
  241.     if (release)
  242.     {
  243.         fname = fp->fl_name;
  244.         delete = fp->fl_close;
  245.         ReleaseMemoryCell ((void *)fp->fl_fd);
  246.  
  247. /* Scan the list and remove the entry */
  248.  
  249.         while (fp1 != (s_flist *)NULL)
  250.         {
  251.         if (fp1 != fp)
  252.         {
  253.             last = fp1;
  254.             fp1 = fp1->fl_next;
  255.             continue;
  256.         }
  257.  
  258.         if (last == (s_flist *)NULL)
  259.             list_start = fp->fl_next;
  260.  
  261.         else
  262.             last->fl_next = fp->fl_next;
  263.  
  264.         break;
  265.         }
  266.  
  267. /* OK - delete the area */
  268.  
  269.         ReleaseMemoryCell ((void *)fp);
  270.     }
  271.     }
  272.  
  273. /* Flush these two in case they were re-directed */
  274.  
  275.     fflush (stdout);
  276.     fflush (stderr);
  277.  
  278. /* Close the file anyway */
  279.  
  280.     if (c_flag)
  281.     {
  282.     i = close (fid);
  283.     serrno = errno;
  284.     }
  285.  
  286. /* Delete the file ? */
  287.  
  288.     if (delete)
  289.     {
  290.     unlink (fname);
  291.     ReleaseMemoryCell ((void *)fname);
  292.     }
  293.  
  294. /* Restore signals */
  295.  
  296.     signal (SIGINT, save_signal);
  297.  
  298. /* Restore results and error code */
  299.  
  300.     errno = serrno;
  301.     return i;
  302. }
  303.  
  304. /*
  305.  * Duplicate file handler.  Add the new handler to the ID array for this
  306.  * file.
  307.  */
  308.  
  309. int S_dup (int old_fid)
  310. {
  311.     int        new_fid;
  312.  
  313.     if ((new_fid = dup (old_fid)) >= 0)
  314.     S_Remap (old_fid, new_fid);
  315.  
  316.     return new_fid;
  317. }
  318.  
  319. /*
  320.  * Add the ID to the ID array for this file
  321.  */
  322.  
  323. int S_Remap (int old_fid, int new_fid)
  324. {
  325.     s_flist    *fp = find_entry (old_fid);
  326.     int        *flist;
  327.     int        i;
  328.  
  329.     if (fp == (s_flist *)NULL)
  330.     return new_fid;
  331.  
  332. /* Is there an empty slot ? */
  333.  
  334.     for (i = 0; i < fp->fl_count; i++)
  335.     {
  336.     if (fp->fl_fd[i] == -1)
  337.         return (fp->fl_fd[i] = new_fid);
  338.     }
  339.  
  340. /* Is there any room at the end ? No - grap somemore space and effect a
  341.  * re-alloc.  What to do if the re-alloc fails - should really get here.
  342.  * Safty check only??
  343.  */
  344.  
  345.     if (fp->fl_count == fp->fl_size)
  346.     {
  347.     if ((flist = (int *) GetAllocatedSpace ((fp->fl_size + F_START) *
  348.                         sizeof (int))) == (int *)NULL)
  349.         return new_fid;
  350.  
  351.     SetMemoryAreaNumber ((void *)flist, 0);
  352.     memcpy ((char *)flist, (char *)fp->fl_fd, sizeof (int) * fp->fl_size);
  353.     ReleaseMemoryCell ((void *)fp->fl_fd);
  354.  
  355.     fp->fl_fd   = flist;
  356.     fp->fl_size += F_START;
  357.     }
  358.  
  359.     return (fp->fl_fd[fp->fl_count++] = new_fid);
  360. }
  361.  
  362. /*
  363.  * Set Delete on Close flag
  364.  */
  365.  
  366. void S_Delete (int fid)
  367. {
  368.     s_flist    *fp = find_entry (fid);
  369.  
  370.     if (fp != (s_flist *)NULL)
  371.     fp->fl_close = TRUE;
  372. }
  373.  
  374. /*
  375.  * Duplicate file handler onto specific handler
  376.  */
  377.  
  378. int S_dup2 (int old_fid, int new_fid)
  379. {
  380.     int        res = -1;
  381.     int        i;
  382.     Save_IO    *sp;
  383.  
  384. /* If duping onto stdin, stdout or stderr, Search the Save IO stack for an
  385.  * entry matching us
  386.  */
  387.  
  388.     if ((new_fid >= STDIN_FILENO) && (new_fid <= STDERR_FILENO))
  389.     {
  390.     for (sp = SSave_IO, i = 0; (i < NSave_IO_E) &&
  391.                    (SSave_IO[i].depth < Execute_stack_depth);
  392.          i++);
  393.  
  394. /* If depth is greater the Execute_stack_depth - we should panic as this
  395.  * should not happen.  However, for the moment, I'll ignore it
  396.  */
  397.  
  398. /* If there an entry for this depth ? */
  399.  
  400.     if (i == NSave_IO_E)
  401.     {
  402.  
  403. /* Do we need more space? */
  404.  
  405.         if (NSave_IO_E == MSave_IO_E)
  406.         {
  407.         sp = (Save_IO *)GetAllocatedSpace ((MSave_IO_E + SSAVE_IO_SIZE)
  408.                             * sizeof (Save_IO));
  409.  
  410. /* Check for error */
  411.  
  412.         if (sp == (Save_IO *)NULL)
  413.             return -1;
  414.  
  415. /* Save original data */
  416.  
  417.         if (MSave_IO_E != 0)
  418.         {
  419.             memcpy (sp, SSave_IO, sizeof (Save_IO) * MSave_IO_E);
  420.             ReleaseMemoryCell ((void *)SSave_IO);
  421.         }
  422.  
  423.         SetMemoryAreaNumber ((void *)sp, 1);
  424.         SSave_IO = sp;
  425.         MSave_IO_E += SSAVE_IO_SIZE;
  426.         }
  427.  
  428. /* Initialise the new entry */
  429.  
  430.         sp = &SSave_IO[NSave_IO_E++];
  431.         sp->depth             = Execute_stack_depth;
  432.         sp->fp[STDIN_FILENO]  = -1;
  433.         sp->fp[STDOUT_FILENO] = -1;
  434.         sp->fp[STDERR_FILENO] = -1;
  435.     }
  436.  
  437.     if (sp->fp[new_fid] == -1)
  438.         sp->fp[new_fid] = ReMapIOHandler (new_fid);
  439.  
  440.     fflush (stdout);
  441.     fflush (stderr);
  442.     }
  443.  
  444. /* OK - Dup the descriptor */
  445.  
  446.     if ((old_fid != -1) && ((res = dup2 (old_fid, new_fid)) >= 0))
  447.     {
  448.     S_close (new_fid, FALSE);
  449.     res = S_Remap (old_fid, new_fid);
  450.     }
  451.  
  452.     return res;
  453. }
  454.  
  455. /*
  456.  * Restore the Stdin, Stdout and Stderr to original values.  If change is
  457.  * FALSE, just remove entries from stack.  A special case for exec.
  458.  */
  459.  
  460. int RestoreStandardIO (int rv, bool change)
  461. {
  462.     int        j, i;
  463.     Save_IO    *sp;
  464.  
  465. /* Start at the top and remove any entries above the current execute stack
  466.  * depth
  467.  */
  468.  
  469.     for (j = NSave_IO_E; j > 0; j--)
  470.     {
  471.        sp = &SSave_IO[j - 1];
  472.  
  473.        if (sp->depth < Execute_stack_depth)
  474.        break;
  475.  
  476. /* Reduce number of entries */
  477.  
  478.     --NSave_IO_E;
  479.  
  480. /* If special case (changed at this level) - continue */
  481.  
  482.     if (!change && (sp->depth == Execute_stack_depth))
  483.         continue;
  484.  
  485. /* Close and restore any files */
  486.  
  487.     for (i = STDIN_FILENO; i <= STDERR_FILENO; i++)
  488.     {
  489.         if (sp->fp[i] != -1)
  490.         {
  491.         S_close (i, TRUE);
  492.         dup2 (sp->fp[i], i);
  493.         S_close (sp->fp[i], TRUE);
  494.         }
  495.     }
  496.     }
  497.  
  498.     return rv;
  499. }
  500.  
  501. /*
  502.  * Create a Pipe
  503.  */
  504.  
  505. int OpenAPipe (void)
  506. {
  507.     register int    i;
  508.  
  509.     if ((i = S_open (TRUE, GenerateTemporaryFileName (), O_PMASK, 0600)) < 0)
  510.     PrintErrorMessage (nopipe);
  511.  
  512.     return i;
  513. }
  514.  
  515. /*
  516.  * Close a pipe
  517.  */
  518.  
  519. void CloseThePipe (register int pv)
  520. {
  521.     if (pv != -1)
  522.     S_close (pv, TRUE);
  523. }
  524.  
  525. /*
  526.  * Check for restricted shell
  527.  */
  528.  
  529. bool CheckForRestrictedShell (char *s)
  530. {
  531.     if (RestrictedShellFlag)
  532.     {
  533.     PrintErrorMessage (BasicErrorMessage, s, "restricted");
  534.     return TRUE;
  535.     }
  536.  
  537.     return FALSE;
  538. }
  539.  
  540. /*
  541.  * Check to see if a file is a shell script.  If it is, return the file
  542.  * handler for the file
  543.  */
  544.  
  545. int OpenForExecution (char *path, char **params, int *nargs)
  546. {
  547.     int        i = -1;
  548.     char    *local_path;
  549.  
  550. /* Work on a copy of the path */
  551.  
  552.     if ((local_path = AllocateMemoryCell (strlen (path) + 4)) == (char *)NULL)
  553.     return -1;
  554.  
  555. /* Try the file name and then with a .sh appended */
  556.  
  557.     if ((i = CheckForScriptFile (strcpy (local_path, path), params, nargs)) < 0)
  558.     i = CheckForScriptFile (strcat (local_path, SHELLExtension), params,
  559.                 nargs);
  560.  
  561.     ReleaseMemoryCell ((void *)local_path);
  562.     return i;
  563. }
  564.  
  565. /*
  566.  * Check for shell script
  567.  */
  568.  
  569. int CheckForScriptFile (char *path, char **params, int *nargs)
  570. {
  571.     char    buf[512];        /* Input buffer            */
  572.     int        fp;            /* File handler            */
  573.     int        nbytes;            /* Number of bytes read        */
  574.     char    *bp;            /* Pointers into buffers    */
  575.     char    *ep;
  576.  
  577.     if ((fp = S_open (FALSE, path, O_RMASK)) < 0)
  578.     return -1;
  579.  
  580. /* zero or less bytes - not a script */
  581.  
  582.     memset (buf, 0, 512);
  583.     nbytes = read (fp, buf, 512);
  584.  
  585.     for (ep = &buf[nbytes], bp = buf; (bp < ep) && ((unsigned char)*bp >= 0x08); ++bp);
  586.  
  587. /* If non-ascii file or lenght is less than 1 - not a script */
  588.  
  589.     if ((bp != ep) || (nbytes < 1))
  590.     {
  591.     S_close (fp, TRUE);
  592.     return -1;
  593.     }
  594.  
  595. /* Ensure end of buffer detected */
  596.  
  597.     buf[511] = 0;
  598.  
  599. /* Initialise the return parameters, if specified */
  600.  
  601.     if (params != (char **)NULL)
  602.     *params = null;
  603.  
  604.     if (nargs != (int *)NULL)
  605.     *nargs = 0;
  606.  
  607. /* We don't care how many bytes were read now, so use it to count the
  608.  * additional arguments
  609.  */
  610.  
  611.     nbytes = 0;
  612.  
  613. /* Find the end of the first line */
  614.  
  615.     if ((bp = strchr (buf, '\n')) != (char *)NULL)
  616.     *bp = 0;
  617.  
  618.     bp = buf;
  619.     ep = (char *)NULL;
  620.  
  621. /* Check for script */
  622.  
  623.     if ((*(bp++) != '#') || (*(bp++) != '!'))
  624.     return fp;
  625.  
  626.     while (*bp)
  627.     {
  628.     while (isspace (*bp))
  629.         ++bp;
  630.  
  631. /* Save the start of the arguments */
  632.  
  633.     if (*bp)
  634.     {
  635.         if (ep == (char *)NULL)
  636.         ep = bp;
  637.  
  638. /* Count the arguments */
  639.  
  640.         ++nbytes;
  641.     }
  642.  
  643.     while (!isspace (*bp) && *bp)
  644.         ++bp;
  645.     }
  646.  
  647. /* Set up the return parameters, if appropriate */
  648.  
  649.     if ((params != (char **)NULL) && (strlen (ep) != 0))
  650.     {
  651.     if ((*params = AllocateMemoryCell (strlen (ep) + 1)) == (char *)NULL)
  652.     {
  653.         *params = null;
  654.         S_close (fp, TRUE);
  655.         return -1;
  656.     }
  657.  
  658.     strcpy (*params, ep);
  659.     }
  660.  
  661.     if (nargs != (int *)NULL)
  662.     *nargs = nbytes;
  663.  
  664.     return fp;
  665. }
  666.  
  667. /*
  668.  * The specified file handler the console
  669.  */
  670.  
  671. bool IsConsole (int fp)
  672. {
  673. #ifdef OS2
  674.     USHORT    fsType;
  675.     USHORT    usDeviceAttr;
  676.  
  677.     DosQHandType (fp, &fsType, &usDeviceAttr);
  678.  
  679. /* The value 0x8083 seems to be the value returned by the console */
  680.  
  681.     return (LOBYTE (fsType) == HANDTYPE_DEVICE) && (usDeviceAttr == 0x8083)
  682.         ? TRUE : FALSE;
  683. #else
  684.     union  REGS  r;
  685.  
  686.     r.x.ax = 0x4400;
  687.     r.x.bx = fp;
  688.     intdos (&r, &r);
  689.     return ((r.x.dx & 0x0081) == 0x0081) ? TRUE : FALSE;
  690. #endif
  691. }
  692.  
  693. /*
  694.  * Get the current drive number
  695.  */
  696.  
  697. unsigned int GetCurrentDrive (void)
  698. {
  699. #ifdef OS2
  700.     ULONG    ulDrives;
  701.     USHORT    usDisk;
  702.  
  703.     DosQCurDisk (&usDisk, &ulDrives);        /* gets current drive        */
  704.  
  705.     return (unsigned int)usDisk;
  706. #else
  707.     unsigned int    CurrentDrive;
  708.  
  709.     _dos_getdrive (&CurrentDrive);
  710.  
  711.     return CurrentDrive;
  712. #endif
  713. }
  714.  
  715. /*
  716.  * Set the current drive number and return the number of drives.
  717.  */
  718.  
  719. unsigned int SetCurrentDrive (unsigned int drive)
  720. {
  721. #ifdef OS2
  722.     ULONG    ulDrives;
  723.     USHORT    usDisk;
  724.     int        i;
  725.  
  726.     DosSelectDisk ((USHORT)drive);
  727.  
  728. /* Get the current disk and check that to see the number of drives */
  729.  
  730.     DosQCurDisk (&usDisk, &ulDrives);        /* gets current drive        */
  731.  
  732.     for (i = 25; (!(ulDrives & (1L << i))) && i >= 0; --i)
  733.     continue;
  734.  
  735.     return (unsigned int) i + 1;
  736. #else
  737.     unsigned int    ndrives;
  738.  
  739.     _dos_setdrive (drive, &ndrives);
  740.  
  741.     return ndrives;
  742. #endif
  743. }
  744.  
  745. /*
  746.  * Get and process configuration line:
  747.  *
  748.  * <field> = <field> <field> # comment
  749.  *
  750.  * return the number of fields found.
  751.  */
  752.  
  753. int    ExtractFieldsFromLine (LineFields *fd)
  754. {
  755.     char    *cp;
  756.     int        len;
  757.     Word_B    *wb;
  758.  
  759.     if (fgets (fd->Line, fd->LineLength - 1, fd->FP) == (char *)NULL)
  760.     {
  761.     fclose (fd->FP);
  762.     return -1;
  763.     }
  764.  
  765. /* Remove the EOL */
  766.  
  767.     if ((cp = strchr (fd->Line, '\n')) != (char *)NULL)
  768.     *cp = 0;
  769.  
  770. /* Remove the comments at end */
  771.  
  772.     if ((cp = strchr (fd->Line, '#')) != (char *)NULL)
  773.     *cp = 0;
  774.  
  775. /* Extract the fields */
  776.  
  777.     if (fd->Fields != (Word_B *)NULL)
  778.     fd->Fields->w_nword = 0;
  779.  
  780.     fd->Fields = SplitString (fd->Line, fd->Fields);
  781.  
  782.     if ((fd->Fields == (Word_B *)NULL) || (fd->Fields->w_nword < 2))
  783.     return 1;
  784.  
  785. /* Check for =.  At end of first field? */
  786.  
  787.     wb = fd->Fields;
  788.     len = strlen (wb->w_words[0]) - 1;
  789.  
  790.     if (wb->w_words[0][len] == '=')
  791.     wb->w_words[0][len] = 0;
  792.  
  793. /* Check second field for just being equals */
  794.  
  795.     else if (wb->w_nword < 3)
  796.     wb->w_nword = 1;
  797.     
  798.     if (strcmp (wb->w_words[1], "=") == 0)
  799.     {
  800.     (wb->w_nword)--;
  801.     memcpy ((void *)(wb->w_words + 1), (void *)(wb->w_words + 2),
  802.         (wb->w_nword - 1) * sizeof (void *));
  803.     }
  804.  
  805. /* Check the third field for starting with an equals */
  806.  
  807.     else if (*(wb->w_words[2]) == '=')
  808.     strcpy (wb->w_words[2], wb->w_words[2] + 1);
  809.  
  810.     else
  811.     wb->w_nword = 1;
  812.  
  813.     return wb->w_nword;
  814. }
  815.  
  816. /*
  817.  * Split the string up into words
  818.  */
  819.  
  820. Word_B *SplitString (char *string, Word_B *wb)
  821. {
  822.     while (*string)
  823.     {
  824.     while (isspace (*string))
  825.         *(string++) = 0;
  826.  
  827.     if (*string)
  828.         wb = AddWordToBlock (string, wb);
  829.  
  830.     while (!isspace (*string) && *string)
  831.         ++string;
  832.     }
  833.  
  834.     return wb;
  835. }
  836.  
  837. /*
  838.  * Test to see if a file is a directory
  839.  */
  840.  
  841. bool    IsDirectory (char *Name)
  842. {
  843.     struct stat        s;
  844.  
  845.     return ((stat (Name, &s) == 0) && S_ISDIR (s.st_mode & S_IFMT))
  846.         ? TRUE : FALSE;
  847. }
  848.  
  849. /*
  850.  * Check that filename conforms to DOS format.  Convert additional .'s to ~.
  851.  */
  852.  
  853. char *CheckDOSFileName (char *name)
  854. {
  855.     char    *s;
  856.     char    *s1;
  857.     int        count = 0;
  858.  
  859. /* Find start of file name */
  860.  
  861.     if ((s = strrchr (name, CHAR_UNIX_DIRECTORY)) == (char *)NULL)
  862.     s = name;
  863.     
  864.     else
  865.     ++s;
  866.  
  867. /* Skip over the directory entries */
  868.  
  869.     if ((strcmp (s, ".") == 0) || (strcmp (s, "..") == 0))
  870.     /*SKIP*/;
  871.  
  872. /* Name starts with a dot? */
  873.  
  874.     else if (*s == '.')
  875.     count = 2;
  876.  
  877. /* Count the number of dots */
  878.  
  879.     else
  880.     {
  881.     s1 = s;
  882.  
  883.     while ((s1 = strchr (s1, '.')) != (char *)NULL)
  884.     {
  885.         count++;
  886.         s1++;
  887.     }
  888.     }
  889.  
  890. /* Check the dot count */
  891.  
  892.     if (count > 1)
  893.     {
  894.     if (FL_TEST ('w'))
  895.         fprintf (stderr, "sh: File <%s> has too many dots, changed to ", 
  896.              name);
  897.  
  898. /* Transform the very first if necessary */
  899.  
  900.     if (*s == '.')
  901.         *s = '~';
  902.  
  903.     s1 = s;
  904.     count = 0;
  905.  
  906. /* Convert all except the first */
  907.  
  908.     while ((s1 = strchr (s1, '.')) != (char *)NULL)
  909.     {
  910.         if (++count != 1)
  911.         *s1 = '~';
  912.  
  913.         *s1++;
  914.     }
  915.  
  916.     if (FL_TEST ('w'))
  917.         PrintWarningMessage ("<%s>\n", name);
  918.     }
  919.  
  920. /* Check for double slashes */
  921.   
  922.     s = name;
  923.  
  924.     while ((s = strchr (s, CHAR_UNIX_DIRECTORY)) != (char *)NULL)
  925.     {
  926.     if (*(++s) == CHAR_UNIX_DIRECTORY)
  927.         strcpy (s, s + 1);
  928.     }
  929.  
  930.     return name;
  931. }
  932.  
  933. /*
  934.  * Get a valid numeric value
  935.  */
  936.  
  937. bool    ConvertNumericValue (char *string, long *value, int base)
  938. {
  939.     char    *ep;
  940.  
  941.     *value = strtol (string, &ep, base);
  942.  
  943.     return (*ep) ? FALSE : TRUE;
  944. }
  945.